#ifndef MODBUSUI_H
#define MODBUSUI_H

#include "ui_modbusui.h"
#include <QMainWindow>
#include <QThread>
#include <QSpinBox>
#include <QTimer>
#include <QModbusDataUnit>
#include <sys/time.h>
#include <QVector>
#include <QQueue>
#include <QMetaType>
#include <QVariant>
#include <QLocalServer>
#include <QLocalSocket>

//#define _MODBUS_DEBUG_PRINT_
#define _MODBUS_DISPLAY

#define _MODBUS_START_ADDR_ 0x63
#define _MODBUS_DATA_SIZE_ 0x60
#define _MODBUS_END_ADDR_ _MODBUS_START_ADDR_+_MODBUS_DATA_SIZE_
#define _MODBUS_READ_STEP 0x60

class HexSpinBox;
class QModbusClient;
class ModbusInterface;
class ModbusThread;

struct _modbusWorkStruct{
    bool readOrNot;
    int addr;
    int delayTime;
    quint16 amount;     //read
    QVector <quint16> vec;      //write
};

QT_BEGIN_NAMESPACE
namespace Ui { class ModbusUI; }
QT_END_NAMESPACE

class ModbusUI : public QMainWindow
{
    Q_OBJECT

public:
    ModbusUI(QWidget *parent = nullptr);
    ~ModbusUI();

public slots:
    //向上接口
//    void SL_writeImpulse(int addr);
//    void SL_writeData(int addr, quint16 data);

signals:
    //向上接口
//    void SN_readFinsih(QVector<quint16>);
    //向下接口
    void SN_modbusConnect(QString comText);
    void SN_modbusRead(int addr, quint16 amount, int delayTime);
    void SN_modbusWrite(int addr, quint16 data, int delayTime);
    void SN_modbusWriteArray(int addr, QVector<quint16> data, int delayTime);

private:
    Ui::ModbusUI *ui;

    //modbus
    ModbusInterface * m_modbusInterface;
    ModbusThread *m_modbusThread;

    //add ui
    HexSpinBox *sbhex_writeAddr, *sbhex_readCount, *sbhex_writeData;

    //timer
    QTimer *m_readPeriodTimer;
    QTimer *m_delayTimer1, *m_delayTimer2;

    //LocalServer
    QLocalServer *m_server;
    QLocalSocket *m_clientConnection;

    void startPeriodTimer();
    void stopPeriodTimer();

    //专门为reg的地址提供355（0x163）的数据转化
    inline void ModbusReadReg(int addr, quint16 amount, int delayTime){
        SN_modbusRead(addr+355, amount, delayTime);}

private slots:

    void on_btn_scan_clicked();
    void on_btn_impulse_clicked();
    void on_btn_writetest_clicked();

    void on_btn_readGroup_clicked();
    void on_btn_readtest_clicked();
    void on_sb_stationNum_valueChanged(int arg1);
    void on_btn_startTimer_clicked();

    //localserver slot
    void on_btn_socketConnect_clicked();
    void on_btn_connect_com_clicked();
    void on_btn_connect_tcp_clicked();
    void on_tabWidget_currentChanged(int index);
//    void on_checkBox_stateChanged(int arg1);
    void on_cb_displaymsg_stateChanged(int arg1);
};

class ModbusInterface: public QThread
{
    Q_OBJECT
public:
    ModbusInterface();
    ~ModbusInterface();
    int m_stationNum;
    QQueue<struct _modbusWorkStruct> m_queueModbusWork;
    QModbusClient *modbusDevice;

public slots:
    void SL_modbusRead(int addr, quint16 amount, int delayTime);
    void SL_modbusWrite(int addr, quint16 data, int delayTime);
    void SL_modbusWriteArray(int addr, QVector<quint16> data, int delayTime);

signals:
    void SN_readFinsih(QByteArray);
    void SN_queuePop();
    void SN_queuePush();
    //1-ui->pte_readText;   2-ui->pte_readText2;    3-ui->statusBar;
    void SN_printMsg(int, QString);
    //1-ProtocolError;  2-other error
    void SN_modbusError(int);

private:
    QTimer *m_timerModbusDelay;

    QVector<quint16> m_vecReadBuf;
    bool m_flagModbusWork;

    QByteArray m_byteArraySendData;

    void modbusStart();
};


class HexSpinBox : public QSpinBox
{
public:
    HexSpinBox(bool only16Bits, QWidget *parent = 0) : QSpinBox(parent), m_only16Bits(only16Bits)
    {
        setPrefix("0x");
        setDisplayIntegerBase(16);
        if (only16Bits)
            setRange(0, 0xFFFF);
        else
            setRange(INT_MIN, INT_MAX);
    }
    unsigned int hexValue() const
    {
        return u(value());
    }
    void setHexValue(unsigned int value)
    {
        setValue(i(value));
    }
protected:
    QString textFromValue(int value) const
    {
        return QString::number(u(value), 16).toUpper();
    }
    int valueFromText(const QString &text) const
    {
        return i(text.toUInt(0, 16));
    }
    QValidator::State validate(QString &input, int &pos) const
    {
        QString copy(input);
        if (copy.startsWith("0x"))
            copy.remove(0, 2);
        pos -= copy.size() - copy.trimmed().size();
        copy = copy.trimmed();
        if (copy.isEmpty())
            return QValidator::Intermediate;
        input = QString("0x") + copy.toUpper();
        bool okay;
        unsigned int val = copy.toUInt(&okay, 16);
        if (!okay || (m_only16Bits && val > 0xFFFF))
            return QValidator::Invalid;
        return QValidator::Acceptable;
    }

private:
    bool m_only16Bits;
    inline unsigned int u(int i) const
    {
        return *reinterpret_cast<unsigned int *>(&i);
    }
    inline int i(unsigned int u) const
    {
        return *reinterpret_cast<int *>(&u);
    }

};

#endif // MODBUSUI_H
